<?php

namespace Src\Libraries\Classes;

class View
{
    public $view;
    public $data;
    public $format;
    public $args;

    public function __construct($args = array()) {
        $this->args = $args;
    }

    public static function make($viewName = null, $return = false)
    {
        if ( !defined('APP_VIEW_PATH') ) {
            throw new InvalidArgumentException("APP_VIEW_PATH is undefined!");
        }

        if ( ! $viewName ) {
            throw new InvalidArgumentException("View name can not be empty!");
        } else {
            $viewFilePath = self::getFilePath($viewName);
            if ( is_file($viewFilePath) ) {
                $view = new View();
                $view->view = $viewFilePath;
                return $view;
            } else {
                throw new UnexpectedValueException("View file does not exist!");
            }
        }
    }

    public function complete()
    {
        return $this->process($this, true);
    }

    public function show()
    {
        return $this->process($this, false);
    }

    public static function json($arr, $return = false)
    {
        if ( !is_array($arr) ) {
            throw new UnexpectedValueException("View::json can only recieve Array!");
        } else {
            $view = new View();
            $view->format = 'json';
            $view->view = $arr;
            return $view->process($view, $return);
        }
    }

    public static function xml($arr = null, $return = false, $structure = null, $basenode = 'xml')
    {
        $view = new View(array('structure' => $structure, 'basenode' => $basenode));
        $view->format = 'xml';
        $view->view = $arr;
        return $view->process($view, $return);
    }

    public static function serialize($arr, $return = false)
    {
        if ( !is_array($arr) ) {
            throw new UnexpectedValueException("View::json can only recieve Array!");
        } else {
            $view = new View();
            $view->format = 'serialize';
            $view->view = $arr;
            return $view->process($view, $return);
        }
    }

    public static function process($view, $return = false)
    {
        if (!$view->format) {
            if ( $view instanceof View ) {
                extract($view->data);
                if($return) {
                    ob_start();
                    require $view->view;
                    $output = ob_get_contents();
                    ob_end_clean();
                    return $output;
                }
                require $view->view;
            } else {
                throw new UnexpectedValueException("\$view must be instance of View!");
            }
        } else if ( $view->format && $view->format == 'json' ) {
            if($return) {
                return json_encode($view->view);
            }
            echo json_encode($view->view);
        } else if ( $view->format && $view->format == 'serialize' ) {
            if($return) {
                return serialize($view->view);
            }
            echo serialize($view->view);
        } else if ( $view->format && $view->format == 'xml' ) {
            $data = $view->view;
            if($view->args) {
                extract($view->args);
            }
            // turn off compatibility mode as simple xml throws a wobbly if you don't.
            if (ini_get('zend.ze1_compatibility_mode') == 1) {
                ini_set('zend.ze1_compatibility_mode', 0);
            }

            if ($structure === null) {
                $structure = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$basenode />");
            }
            // Force it to be something useful
            if ( ! is_array($data) AND ! is_object($data)) {
                $data = (array) $data;
            }

            foreach ($data as $key => $value) {

                //change false/true to 0/1
                if(is_bool($value)) {
                    $value = (int) $value;
                }
                // no numeric keys in our xml please!
                if (is_numeric($key)) {
                    // make string key...
                    $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item';
                }

                // replace anything not alpha numeric
                $key = preg_replace('/[^a-z_\-0-9]/i', '', $key);
                if ($key === '_attributes' && (is_array($value) || is_object($value))) {
                    $attributes = $value;
                    if (is_object($attributes)) $attributes = get_object_vars($attributes);
                    
                    foreach ($attributes as $attributeName => $attributeValue) {
                        $structure->addAttribute($attributeName, $attributeValue);
                    }
                // if there is another array found recursively call this function
                } elseif (is_array($value) || is_object($value)) {
                    $node = $structure->addChild($key);
                    // recursive call.
                    $this->to_xml($value, $node, $key);
                } else {
                    // add single node.
                    $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8");
                    $structure->addChild($key, $value);
                }
            }

            if($return) {
                return $structure->asXML();
            }
            echo $structure->asXML();
        }
    }

    public function with($key, $value = null)
    {
        $this->data[$key] = $value;
        return $this;
    }

    private static function getFilePath($viewName)
    {
        $filePath = str_replace('.', '/', $viewName);
        return APP_VIEW_PATH.$filePath.'.php';
    }

    public function __call($method, $parameters)
    {
        if (starts_with($method, 'with')) {
            return $this->with(snake_case(substr($method, 4)), $parameters[0]);
        }

        throw new BadMethodCallException("Function [$method] does not exist!");
    }
}